Skip to content

M8: verification diagnostics (snapshot-based analyzer)#8

Merged
lgamorim merged 7 commits into
masterfrom
feature/M8-verification-diagnostics
Jul 11, 2026
Merged

M8: verification diagnostics (snapshot-based analyzer)#8
lgamorim merged 7 commits into
masterfrom
feature/M8-verification-diagnostics

Conversation

@lgamorim

Copy link
Copy Markdown
Owner

The analyzer side of ADR 0001: compare each [SqlQuery]/[SqlExecute] method's declared signature against the database-described metadata committed under .sqlbound/ — fast, offline, never a database connection. Ships inside SqlBound.Generators (0.3.0-preview.2).

What's here

  • SqlQueryVerificationAnalyzer: parses attributed methods with the generator's own parser (a symbol-based entry point extracted as a pure refactor, pinned by the golden emission tests), pairs each query with its snapshot by SHA-256 of the raw command text (.sqlbound/query-<hash>.json, naming provisional until M9), and reports findings at the method identifier. Usage errors (SQLB0xx) remain the generator's — the analyzer never double-reports.
  • ADR 0003 — opt-in by snapshot presence: zero .sqlbound/ AdditionalFiles → total silence, so codegen-only consumers are never nagged; any snapshot present → a query without one is SQLB101 (warning), surfacing the forgot-to-re-run-prepare hole in the IDE immediately. Severities retunable via .editorconfig.
  • QueryVerifier (pure, location-free): columns match by case-insensitive name, not ordinal (generated code binds with GetOrdinal); types compare as mapped CLR types, not SQL type names (SQL Server's suggested types are inferences — the decimal(38,19) lesson from M7); over-nullable declarations are deliberately silent.
  • QuerySnapshotReader: snapshot JSON via a small hand-rolled parser instead of a System.Text.Json dependency — analyzer dependencies must ship bundled and are a known source of IDE version conflicts. Malformed input becomes SQLB102, never a crash; unknown fields are ignored so M9 can extend the schema.
  • docs/diagnostics.md: full SQLB catalog.

Diagnostics added (SqlBound.Verification)

SQLB101/102 (no snapshot / invalid-stale snapshot, Warning), SQLB103–106 (no result set, missing column, column type, nullability — Error), SQLB107 (unread columns, Info), SQLB108/110 (parameter missing from method / parameter type — Error), SQLB109 (unused method parameter, Warning), SQLB111 ([SqlExecute] discards a result set, Warning).

One refinement over the approved plan: "statement produces no result set" got its own ID rather than one missing-column error per declared column, so the range is 101–111 instead of 101–110.

Testing

  • 42 new unit tests: JSON reader (valid/malformed/escapes/forward-compat), QueryVerifier as a pure function covering every rule and the silent directions, and a new AnalyzerHarness (CompilationWithAnalyzers + in-memory AdditionalText) running the analyzer end-to-end from source + snapshot files — opt-in silence, matching-snapshot silence, SQLB101/102/106/111 paths, and location assertions.
  • No database anywhere; the suite is fast and deterministic. The full describe→snapshot→analyzer loop gets its integration proof in M9 when prepare exists to write real snapshots.
  • Solution-wide: 196 tests green, zero warnings, all five packages pack.

🤖 Generated with Claude Code

lgamorim and others added 7 commits July 11, 2026 21:24
Bump PackageVersion to 0.3.0-preview.2 so M8's verification analyzer
work is distinguishable from the M7 preview in package feeds.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Extract the parsing core out of the GeneratorAttributeSyntaxContext
overload so the upcoming verification analyzer can parse an attributed
method from its IMethodSymbol without a generator pipeline. Pure
refactor; the golden emission tests pin the behavior.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
QuerySnapshot models the analyzer-side view of one .sqlbound/ file and
QuerySnapshotReader parses it with a small hand-rolled JSON parser.
A library dependency was rejected deliberately: analyzer dependencies
must ship bundled and are a known source of version conflicts inside
the IDE, while this schema is tiny and written by SqlBound's own
prepare step. Unknown fields are ignored for forward compatibility;
malformed input returns false so the analyzer can diagnose, not crash.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
QueryVerifier compares a parsed query method with its snapshot and
reports SQLB103-107: no result set at all, missing named columns, CLR
type mismatches, database-nullable columns declared non-nullable, and
unread result columns. Matching is by case-insensitive name because
the generated code binds with GetOrdinal, and by mapped CLR type
because the database's SQL type suggestions are inferences. The
over-nullable direction (declared nullable, column non-nullable) is
deliberately silent - it is safe at runtime.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
SQLB108-111 complete the comparison rules: a statement parameter with
no matching method parameter is an error (guaranteed runtime failure),
a method scalar the SQL never uses is a warning, parameter CLR type
mismatches are errors, and an [SqlExecute] statement that returns a
result set warns that the rows are silently discarded. Infrastructure
parameters (connection, transaction, cancellation token) are excluded
from matching.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
SqlQueryVerificationAnalyzer closes ADR 0001's loop: it parses each
attributed method with the generator's own parser, pairs it with its
.sqlbound/ snapshot by SHA-256 of the raw command text, and reports
QueryVerifier findings at the method identifier. Verification is
opt-in by presence: zero .sqlbound/ AdditionalFiles means total
silence for codegen-only consumers, while any wired snapshot makes a
query without one SQLB101 (stale coverage) and an unreadable or
text-mismatched file SQLB102. Usage errors stay the generator's to
report - the analyzer skips methods that produce no model.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ADR 0003 closes the question ADR 0001 deferred: the committed
.sqlbound/ directory itself is the opt-in switch. No snapshots wired
means the analyzer is silent, so codegen-only consumers are never
nagged; any snapshot present makes a query without one a warning,
surfacing the forgot-to-re-run-prepare hole in the IDE immediately.
docs/diagnostics.md catalogs SQLB001-010 and SQLB101-111 with the two
non-obvious comparison rules (name-based column matching, CLR-type
comparison).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@lgamorim lgamorim merged commit 9c0bb88 into master Jul 11, 2026
4 checks passed
@lgamorim lgamorim deleted the feature/M8-verification-diagnostics branch July 11, 2026 21:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant